import json
import pandas as pd  # type:ignore
from concurrent.futures import ThreadPoolExecutor, wait, FIRST_COMPLETED
from typing import Literal, List, Generator, Any, Tuple, cast, Callable
import requests
import re
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64

# 请求接口模板
from .rjmart.pre_api_cx import pre_api_get as pre_api_cx
from .rjmart.parameModel import *


class Base_RJ(object):
    """
    锐竞操作功能外的补充操作以及全局参数配置，不要单独创建实例

    参数：
    :param brandIds: 系统中的默认品牌id，默认为0，表示无效
    :param max_workers: 程序允许启用的最大线程数
    """

    def __init__(self,
                 brandIds: List[int] = [],
                 max_workers: int = 3) -> None:

        class api:

            def __init__(self):
                self.rj_cx_api = pre_api_cx()

        self.list_: List = []
        self.DEFAULT_RULES = lambda data, api, self: data
        self.rules = self.DEFAULT_RULES
        self.ProductList = pd.DataFrame([])
        self.brandIds = brandIds
        self.max_workers = max_workers
        self.rj_apis = api()
        self.rj_api = self.rj_apis.rj_cx_api
        self.header = {
            "User-Agent":
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0",
        }
        self.UnitAndSpec = {
            "包": 1,
            "把": 1,
            "袋": 1,
            "例": 1,
            "mol": 0,
            "un": 0,
            "mg": 0,
            "ea": 0,
            "ml": 0,
            "张": 1,
            "mm": 0,
            "管": 1,
            "次": 1,
            "plates": 0,
            "test": 0,
            "vial": 1,
            "个": 1,
            "株": 1,
            "μg": 0,
            "pmol": 0,
            "支": 1,
            "箱": 1,
            "根": 1,
            "组": 1,
            "片": 1,
            "mmol": 0,
            "盒": 1,
            "套": 1,
            "块": 1,
            "µl": 0,
            "µm": 0,
            "kg": 0,
            "条": 1,
            "nmoles": 0,
            "µmol": 0,
            "g": 0,
            "只": 1,
            "cm": 0,
            "ku": 0,
            "l": 0,
            "nmol": 0,
            "台": 1,
            "t": 1,
            "u": 0,
            "瓶": 1,
            "件": 1,
            "桶": 1,
            "卷": 1,
            "项": 1,
            "批": 1,
            "对": 0,
            "份": 1,
            "板": 0,
        }

    def __str__(self) -> str:
        return "单独初始化该参数类为无效行为"

    # ^切换锐竞接口到学人
    def switchToXr(self):
        self.rj_api = self.rj_apis.rj_xr_api
        return self

    # ^切换锐竞接口到辰星
    def switchToCx(self):
        self.rj_api = self.rj_apis.rj_cx_api
        return self

    # ^修改数据模型等价装换
    def dataModelConversionUpdate(
            self,
            productsData: dict) -> Tuple[rjUpdateProductModel, bool, str]:
        """
        修改数据模型等价装换

        参数：
            :param productsData: 商品需要修改的信息，模板如下，其中商品货号是必传字段
            >>> productsData= [{
                    "productNum": "",  #! 商品货号（必须要具有商品唯一性）
                    "price":  0.00,  # * 统一售价, 保留两位小数(若商品品牌已签署平台协议价, 则售价不能高于“协议上限折扣价”(协议上限折扣价 = 目录价 * 协议价上限折扣))
                    "directoryPrice": 0,  # *目录价, 保留两位小数(若商品品牌签署了协议价必传, 未签署选传)
                    "sku": 0,  # *库存数量
                    "unit": "",  # *销售单位, 不可自定义, 必须使用平台的标准销售单位 (数据来源: 查询标准单位、包装规格和最小包装规格列表的接口/openapi/product/queryUnitAndSpecification)
                    "desc": """ """,  # *	商品介绍 (支持富文本格式)
                    "photos": [{"photo":"url"}],  #*商品图库 (程序会截取第一张图为主图 , 一共不能超过5张图) , 见下方ProductPhotoDTO
                    "deliveryTime": 0,  # *货期[[[1	现货],[7	1周内],[14	2周],[21	3周],[30	1个月],[60	2个月],[61	2个月以上],[-1	咨询供应商客服]]]
                    "carryFeeTemplateId": 0,  # *运费模版Id, 全国包邮不用传(数据来源 : 查询商家所有运费模板/openapi/product/findTemplate)
                    "attributes": [{"attributeName":"","attributeNameValue":""}], # *商品属性模型 , 见下方AttributesVO
                    }]
            :returns: returns[0]:锐竞修改参数模型实例  returns[1]:是否成功处理 returns[2]:处理后错误消息
        """
        RjUpdateProductModelKeys = rjUpdateProductModel.__annotations__
        product: rjUpdateProductModel = {}

        for prodectData in productsData:
            if prodectData not in RjUpdateProductModelKeys.keys():
                continue
            else:
                product[prodectData] = productsData[prodectData]  # type:ignore

        return product, True, ""

    # ^上传数据模型等价装换
    def dataModelConversion(
            self, productDetail: rjObjectParamModel) -> rjPutParamsModel:
        """
        上传数据模型等价装换

        参数：
        :param productDetail: 商品详情信息，详见parameModel模板库中ObjectParamModel模块

        >>> from rjmart.parameModel import ObjectParamModel

        :param Object: 仅接受[RJExportDataModel, RJReptilesModel, RJPutParamsModel, RjUpdateProductModel]模板生成的实例
        :param Type_: 模板类名，供函数识别后对应模板匹配输出["RJExportDataModel", "RJReptilesModel", "RJPutParamsModel"]
        :return: 返回锐竞上传接口的合法模型(`rjPutParamsModel`)实例
        """
        if productDetail["Type_"] == "RJExportDataModel":
            productRJExportDataModel = cast(rjExportDetailDataModel,
                                            productDetail["Object"])

            result = rjPutParamsModel({
                "attributes": [
                    i for i in
                    (productRJExportDataModel["attributes"] if isinstance(
                        productRJExportDataModel["attributes"], list) else [])
                    if i["attributeNameValue"]
                ],
                **({
                    "brandId": brandid["id"]
                } if (brandid := productRJExportDataModel["brand"]) else {}),
                "categoryId":
                productRJExportDataModel["category"][-1]["id"],
                "deliveryTime":
                productRJExportDataModel["deliveryTime"],
                "desc":
                productRJExportDataModel["desc"],
                "name":
                productRJExportDataModel["name"],
                "photos":
                productRJExportDataModel["photos"][:5],
                "price":
                productRJExportDataModel["price"],
                "productNum":
                productRJExportDataModel["productNum"],
                "sku":
                productRJExportDataModel["sku"],
                "unit":
                productRJExportDataModel["unit"],
                "status":
                productRJExportDataModel["status"],
                "specification":
                productRJExportDataModel["specification"],
                # "productSpecificationDTO": productRJExportDataModel[
                #     "productSpecificationDTO"
                # ]
                # if productRJExportDataModel["productSpecificationDTO"][
                #     "minSpecificationUnit"  # type:ignore
                # ]
                # else PutProductSpecificationDTO(
                #     {
                #         "packingSpecificationUnit": productRJExportDataModel[
                #             "productSpecificationDTO"
                #         ][
                #             "packingSpecificationUnit"
                #         ],  # type:ignore
                #         "packingSpecificationValue": productRJExportDataModel[
                #             "productSpecificationDTO"
                #         ][
                #             "packingSpecificationValue"
                #         ],  # type:ignore
                #     }
                # ),
                "carryFeeTemplateId":
                productRJExportDataModel["carryFeeTemplateId"],
                **({
                    "dangerousDto":
                    putdangerousDto({
                        "casNo":
                        productRJExportDataModel["dangerousDto"]["casNo"]
                    })
                } if productRJExportDataModel["dangerousDto"].get(
                       "casNo", False) else {}),
            })
            for pro in productRJExportDataModel:
                # print(pro, rjPutParamsModel.__annotations__.keys())
                if pro != "id" and pro in rjPutParamsModel.__annotations__.keys(
                ):
                    result[pro] = productRJExportDataModel[pro]

            return result
        elif productDetail["Type_"] == "RJReptilesModel":
            productRJReptilesModel = cast(rjReptilesModel,
                                          productDetail["Object"])
            rjputparamsmodel = rjPutParamsModel({
                "attributes":
                (productRJReptilesModel["attributes"] if isinstance(
                    productRJReptilesModel["attributes"], dict) else None),
                "brandId":
                productRJReptilesModel["brandId"],
                "categoryId":
                productRJReptilesModel["categoryId"],
                "deliveryTime":
                productRJReptilesModel["deliveryTime"],
                "desc":
                productRJReptilesModel["description"],
                "name":
                productRJReptilesModel["name"],
                "photos":
                ([{
                    "photo": photo
                } for photo in photos] if
                 (photos := productRJReptilesModel["photos"]) else []),
                "price":
                productRJReptilesModel["price"],
                "productNum":
                productRJReptilesModel["code"],
                "sku":
                sku if (sku := productRJReptilesModel["sku"]) else 99,
                "unit":
                productRJReptilesModel["unit"],
                "status":
                productRJReptilesModel["status"],
                "specification":
                productRJReptilesModel["specification"],
                # "productSpecificationDTO": PutProductSpecificationDTO(),
                "carryFeeTemplateId":
                productRJReptilesModel["carryFeeTemplateId"],
                # "priceDifferents": None,
            })

            if productRJReptilesModel["casNo"]:  # ^添加cas号
                rjputparamsmodel["dangerousDto"] = putdangerousDto(
                    casNo=productRJReptilesModel["casNo"])
                if rjputparamsmodel["attributes"]:
                    rjputparamsmodel["attributes"].append(
                        putAttributesVO(
                            attributeName="casNo",
                            attributeNameValue=productRJReptilesModel["casNo"],
                        ))
                else:
                    rjputparamsmodel["attributes"] = [
                        putAttributesVO(
                            attributeName="casNo",
                            attributeNameValue=productRJReptilesModel["casNo"],
                        )
                    ]
            if productRJReptilesModel["category"][0]["id"] == 51:  # ^一级分类为化学试剂
                if "/" in productRJReptilesModel["specification"]:
                    specification = productRJReptilesModel["specification"]
                    if "，" in specification:
                        specification = specification.split("，")[0]
                    elif "," in specification:
                        specification = specification.split(",")[0]
                    specificationList = specification.split("/")
                    if self.UnitAndSpec[rjputparamsmodel["unit"]]:
                        rjputparamsmodel["productSpecificationDTO"] = (
                            putProductSpecificationDTOmin(
                                packingSpecificationValue=1,
                                packingSpecificationUnit=rjputparamsmodel[
                                    "unit"],
                                minSpecificationValue=re.findall(
                                    r"\d*", specificationList[0])[0],
                                minSpecificationUnit=re.findall(
                                    r"\D{1,3}", specificationList[0])[0],
                            ))
                        rjputparamsmodel["unit"] = "件"
                    else:
                        rjputparamsmodel["productSpecificationDTO"] = (
                            putProductSpecificationDTO(
                                packingSpecificationValue=re.findall(
                                    r"\d*", specificationList[0])[0],
                                packingSpecificationUnit=re.findall(
                                    r"[a-z]{1,3}", specificationList[0])[0],
                            ))
            for pro in productRJReptilesModel:
                if ((pro != "id")
                        and (pro not in rjReptilesModel.__annotations__.keys())
                        and (pro in rjPutParamsModel.__annotations__.keys())):
                    rjputparamsmodel[pro] = productRJReptilesModel[pro]
            # print(rjputparamsmodel)
            return rjputparamsmodel
        else:
            productRJPutParamsModel = cast(rjPutParamsModel,
                                           productDetail["Object"])
            return productRJPutParamsModel

    # ^保存商品列表为CSV文件
    def save_to_csv(self, filename: str = "", **kwargs) -> tuple[bool, str]:
        """
        保存商品列表为CSV文件

        :param filename:文件名称，后缀已经规定为".csv"
        :param kwargs: 伸展参数，自行补充
        "returns: returns[0]:是否保存成功 returns[1]:保存失败的错误消息
        """
        if not filename:
            return False, "保存文件名不明"
        try:
            self.ProductList = pd.DataFrame(self.list_)
            self.ProductList.to_csv(filename + ".csv", **kwargs)
        except Exception as e:
            return False, str(e)
        return True, "保存成功"

    def _error_try(self, funcName: Union[str, None] = None) -> Callable:
        """
        错误尝试

        :param funcName:错误尝试的提示函数名,没有实际影响
        :retuen : None
        """

        def callable_(func: Callable) -> Any:
            """
            错误重试

            :param func:需要自动重启的函数
            :return: None
            """

            def callable(*args, **kwargs) -> func:
                if not funcName:
                    name = func.__name__
                else:
                    name = funcName
                tryNum = 0
                while True:
                    try:
                        tryNum += 1
                        return func(*args, **kwargs)
                    except requests.Timeout:
                        print(f"【{name}:超时】->重试")
                        if tryNum == 4:
                            raise requests.Timeout("响应超时超过5次,不再连接")
                    except Exception as e:
                        print(f"【{name}】->重试", e)
                        if tryNum == 4:
                            raise Exception(f"意外错误超过5次【{e}】,不再连接")

            return callable

        return callable_


class RJ_api(Base_RJ):
    """
    锐竞平台官方接口实际业务处理模块

    参数：
    :param brandIds: 系统中的默认品牌id，默认为0，表示无效
    :param max_workers: 程序允许启用的最大线程数
    """

    def __init__(
        self,
        brandIds: List[int] = [],
        max_workers: int = 3,
    ) -> None:
        super(RJ_api, self).__init__(brandIds, max_workers)

    def __str__(self) -> str:
        return """成功创建【锐竞平台官方接口实际业务处理模块】
            brandIds: {}
            max_workers: {}
            list_: {}
            DEFAULT_RULES: {}
            rules: {}
            rj_apis: {}
            rj_api: {}
        """.format(
            self.brandIds,
            self.max_workers,
            self.list_,
            self.DEFAULT_RULES,
            self.rules,
            self.rj_apis,
            self.rj_api,
        )

    # ^获取商品目录列表（全部）
    def official_getProductList(
        self,
        brandIds: List[int] = [],
        status: Literal[2, 3, 4, 5, 12, 15] = None,
        productNum: str = "",
        productName: str = "",
        categoryId: int = 0,
        rule: Callable = lambda data, api, self: data,
    ) -> Generator[List[rjExportDescDataModel], None, None]:
        """
        获取商品目录列表（全部）skuid

        :param status:获取商品列表的商品状态，默认为已上架
        :param productNum:商品货号，支持分词搜索，选填
        :param productName:商品名称，支持分词搜索，选填
        :param brandIds:品牌id，默认为实例注册时的id，如果创建实例时未创建则必传，否则选填
        :param categoryId:商品分类的id，选填
        :return: 搜索完毕的数据列表，并同步到了list_内部变量中
        """
        data_json = {
            "size": "50",
        }
        data_json["brandIds"] = brandIds  # type:ignore
        if categoryId:
            data_json["categoryId"] = categoryId  # type:ignore
        if productName:
            data_json["productName"] = productName
        if productNum:
            data_json["productNum"] = productNum
        if status:
            data_json["status"] = status
        indexNum = ""

        while True:
            data_json["indexNum"] = indexNum
            resp = self.rj_api.ProductList(data_json=data_json).json()
            if not resp["data"]:
                break
            indexNum = resp["data"][-1]["indexNum"]
            ddn = []
            for i in [
                    n for n in
                [rule(j, self.rj_api, self) for j in resp["data"]]
            ]:

                ddn.append(i)
                print(i["id"], "获取列表")

            yield ddn

        # # ^获取商品详情列表（全部）
        # rule: Callable = (lambda data, api, self: data,)

    def official_getProductDetailList(
        self,
        ProductIds: list = [],
        rule: Callable = lambda data, api, self: data,
    ) -> List[rjExportDetailDataModel]:
        """
        获取商品详情列表（全部）
        程序多线程获取详情，线程数由`self.max_workers`控制

        tip: 查询后的列表覆盖上一次的列表，查询前要确认上一次的列表是否保存

        :param ProductIds: 列表中对应商品的`id`列表
        :return: 获取到的商品详情列表，并同步替换入`self.list_`
        """
        print("获取商品详情：【数量：{}】".format(len(ProductIds)))

        def product_detail(ProductId):  #!读取详情
            resp = self.rj_api.ProductDetail(ProductId)
            list_.append(resp.json()["data"])
            print(ProductId, "获取详情")

        if not ProductIds:  #!是否有指定列表
            raise ValueError("商品id列表不能为空")
        list_: List = []
        with ThreadPoolExecutor(
                max_workers=self.max_workers) as threading_executor:  #!多线程调用接口
            works = [
                threading_executor.submit(product_detail, ProductId=ProductId)
                for ProductId in ProductIds
            ]
            wait(
                fs=works,
                return_when=FIRST_COMPLETED,
            )

        return [p for p in [rule(j, self.rj_api, self) for j in list_]]

    # ^下架列表中的商品
    def official_offShelves(
        self,
        data: List[dict] = [{}],
        rule: Callable = lambda data, api, self: data,
    ) -> None:
        """
        下架列表中的商品

        :param data: 上架的商品`id`列表
        :return : None
        """
        print("锐竞下架商品：【数量：{}】".format(len(data)))

        values: List[List] = [[]]
        for i in [i for i in [rule(j, self.rj_api, self) for j in data]]:
            values[-1].append(i)
            if len(values[-1]) == 500:
                values.append([])
        for i in values:
            while (resp := self.rj_api.offSale(i).json())["code"] != 200:
                print(resp["msg"])
            print(resp, "下架")

    # ^上架列表中的商品
    def official_onShelves(
        self,
        data: List[dict] = [{}],
        rule: Callable = lambda data, api, self: data,
    ) -> None:
        """
        上架列表中的商品

        :param data: 上架的商品`id`列表
        :return : None
        """
        print("锐竞上架商品：【数量：{}】".format(len(data)))
        values: List[List] = [[]]
        for i in [i for i in [rule(j, self.rj_api, self) for j in data]]:
            values[-1].append(i)
            if len(values[-1]) == 500:
                values.append([])
        for i in values:
            while (resp := self.rj_api.onSale(i).json())["code"] != 200:
                print(resp["msg"])

            print(resp, "上架")

    # ^删除列表中的商品
    def official_deleteProductList(
        self,
        data: List[int] = [],
        rule: Callable = lambda data, api, self: data,
    ) -> None:
        """
        删除列表中的商品

        :param data: 删除的商品`id`列表
        :return: None
        """
        print("锐竞删除商品：【数量：{}】".format(len(data)))
        values: List[List] = [[]]
        for i in [i for i in [rule(j, self.rj_api, self) for j in data]]:
            values[-1].append(i)
            if len(values[-1]) == 500:
                values.append([])
        for i in values:
            with self.rj_api.deleteProduct(i) as resp:
                print(resp.text, "删除")

    # ^批量修改列表中的商品
    def official_updateProductList(
        self,
        data: List[rjUpdateProductModel] = [],
        rule: Callable = lambda data, api, self: data,
    ) -> None:
        """
        批量修改列表中的商品

        :param data:修改商品的参数实例列表，实例模板请严格遵循`RjUpdateProductModel`参数类型规则
        :return : None
        """
        print("锐竞修改商品：【数量：{}】".format(len(data)))
        values: List[List] = [[]]
        for i in [i for i in [rule(j, self.rj_api, self) for j in data]]:
            values[-1].append(i)
            if len(values[-1]) == 50:
                values.append([])
        for i in values:
            if i:
                data_json = []
                for j in i:
                    data_, ok, err = self.dataModelConversionUpdate(j)
                    if ok:
                        data_json.append(data_)
                    else:
                        print(err)
                with self.rj_api.updateProductInfoByCode(
                        json_data=data_json) as resp:
                    print(resp.text,
                          "批量修改->{}".format(len(i)),
                          data_json,
                          sep="\n")

    # ^批量上传商品
    def official_addProduct(
        self,
        dataList: List[rjObjectParamModel],
        rule: Callable = lambda data, api, self: data,
    ) -> int:
        """
        批量上传商品

        :param dataList: 上传的商品参数实例列表，实例请严格遵循`ObjectParamModel`模板
        :return : 上传成功的数量

        >>> dataList:List[ObjectParamModel]=    {
        >>>     Object: Union[RJReptilesModel, RJExportDataModel, RJPutParamsModel]
        >>>     Type_: Literal["RJExportDataModel", "RJReptilesModel", "RJPutParamsModel"]
        >>>     }
        三种上传模式：
            >>> "RJExportDataModel" #^从自己官网导出的商品信息模板
            >>> "RJReptilesModel" #^从平台的其他店铺爬去的商品信息模板
            >>> "RJPutParamsModel" #^公用的商品上传参数模板，不做信息配对
        """
        print("上传商品：【数量：{}】".format(len_ := len(dataList)))

        successNum = 0

        @self._error_try("批量上传商品")
        def PutProduct(data: rjPutParamsModel):
            with self.rj_api.saveOrUpdateProduct(json_data=data) as resp:
                json_data = resp.json()["code"]
                print(resp.json())

                if json_data == 200:
                    return True
                return False

        for i in [i for i in [rule(j, self.rj_api, self) for j in dataList]]:
            if PutProduct(data=self.dataModelConversion(i)):
                successNum += 1
        print("上传结束：【目标数量：{}】【成功数量：{}】".format(len_, successNum))
        return successNum


class RJ_Other(RJ_api):
    """
    锐竞平台采购人爬虫模块

    参数：
    :param brandIds: 系统中的默认品牌id，默认为0，表示无效
    :param max_workers: 程序允许启用的最大线程数
    """

    def __init__(self, brandIds: List[int] = [], max_workers=3):
        super(RJ_Other, self).__init__(brandIds, max_workers)
        self.purchasingerSession = requests.Session()
        self.purchasingerSession.get(
            url="https://www.rjmart.cn/",
            headers={
                "Accept":
                "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
                "Accept-Encoding":
                "gzip, deflate, br",
                "Accept-Language":
                "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
                "Cache-Control":
                "max-age=0",
                "Connection":
                "keep-alive",
                "Host":
                "www.rjmart.cn",
                "If-Modified-Since":
                "Fri, 01 Mar 2024 11:16:05 GMT",
                "If-None-Match":
                '"65e1b8f5-6ae"',
                "Referer":
                "https://www.rjmart.cn/SP/",
                "Sec-Ch-Ua":
                '"Chromium";v="122", "Not(A:Brand";v="24", "Microsoft Edge";v="122"',
                "Sec-Ch-Ua-Mobile":
                "?0",
                "Sec-Ch-Ua-Platform":
                '"Windows"',
                "Sec-Fetch-Dest":
                "document",
                "Sec-Fetch-Mode":
                "navigate",
                "Sec-Fetch-Site":
                "same-origin",
                "Sec-Fetch-User":
                "?1",
                "Upgrade-Insecure-Requests":
                "1",
                "User-Agent":
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0",
            },
        )  # ^初始化session
        self.rj_urls = {
            "classList": "https://www.rjmart.cn/gaea/detail/category",
            "goodsList": "https://gateway.rjmart.cn/store/athena/search/main",
            "goodsDetail":
            "https://gateway.rjmart.cn/store/cart/getProductDetail",
            "goodsSearch":
            "https://gateway.rjmart.cn/store/athena/search/main",
        }
        self.RJ_headers = {
            "Accept":
            "application/json, text/plain, */*",
            "Content-Type":
            "application/json",
            "Authorization":
            "Bearer YW50Y2Fz.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJyZWFnZW50IiwiaWF0IjoxNzA5MDAyNTI4LCJzdWIiOiI1NzY0NDkzODQzNTEyNzI5NjYiLCJleHAiOjE3MDk2MDczMjgsInVpZCI6IjEwMTA5OSIsInVzZXJfbmFtZSI6IjU3NjQ0OTM4NDM1MTI3Mjk2NiIsImNsaWVudF9pZCI6IndlYmFwcCIsInVzIjoxLCJvcmdJZCI6OTk5LCJ1dCI6MX0.vU1tXwnJIqCfaHYuA_XQQHbEeQ5-CQijqIJLif1gwj8",
            # YW50Y2Fz.bXNoYXJw.ECA37DDE11AEEVZMMLgAObgoG6mKow==
            "Referer":
            "https://www.rjmart.cn/",
            "Host":
            "gateway.rjmart.cn",
            "Origin":
            "https://www.rjmart.cn",
            "User-Agent":
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0",
        }
        self.session = requests.session()
        self.__login()

    def __str__(self) -> str:
        return """成功创建【锐竞平台官方接口实际业务处理模块-锐竞平台爬虫模块】
            brandIds: {}
            max_workers: {}
            list_: {}
            DEFAULT_RULES: {}
            rules: {}
            rj_apis: {}
            rj_api: {}
            urls: {}
            headers: {}
            """.format(
            self.brandIds,
            self.max_workers,
            self.list_,
            self.DEFAULT_RULES,
            self.rules,
            self.rj_apis,
            self.rj_api,
            self.rj_urls,
            self.RJ_headers,
        )

    # ^获取登录公匙
    def __getPubliceKey(self) -> str:
        """获取登录公匙"""
        publickey = self.purchasingerSession.post(
            url="https://www.rjmart.cn/auth/rsa/publickey",
            headers={
                "User-Agent":
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0",
            },
            timeout=10,
        ).text
        publickey = """-----BEGIN PUBLIC KEY-----
                    {}
                    -----END PUBLIC KEY-----""".format(publickey)
        return publickey

    # ^采购人登录
    def __login(self) -> None:
        """采购人登录"""
        # 创建公钥加密对象
        public_key = RSA.import_key(self.__getPubliceKey())
        cipher = PKCS1_v1_5.new(public_key)

        # 这里cipher就是你的加密对象，可以用来加密消息
        # 例如加密一条消息
        obj = {
            "grant_type": "password",
            "username": "18054221537",
            "password": "test123456",
            "type": 1,
        }

        encrypted_message = cipher.encrypt(json.dumps(obj).encode("utf-8"))
        encrypted_data_base64 = base64.b64encode(encrypted_message).decode(
            "utf-8")
        with self.purchasingerSession.post(
                url="https://www.rjmart.cn/auth/oauth/token",
                headers=
            {
                "Accept":
                "application/json, text/plain, */*",
                "Accept-Encoding":
                "gzip, deflate, br",
                "Accept-Language":
                "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
                "Authorization":
                "basic d2ViYXBwOndlYmFwcA==",
                "Connection":
                "keep-alive",
                "Content-Length":
                "743",
                "Content-Type":
                "application/x-www-form-urlencoded",
                "Host":
                "www.rjmart.cn",
                "Origin":
                "https://www.rjmart.cn",
                "Referer":
                "https://www.rjmart.cn/",
                "Sec-Ch-Ua":
                '"Chromium";v="122", "Not(A:Brand";v="24", "Microsoft Edge";v="122"',
                "Sec-Ch-Ua-Mobile":
                "?0",
                "Sec-Ch-Ua-Platform":
                '"Windows"',
                "Sec-Fetch-Dest":
                "empty",
                "Sec-Fetch-Mode":
                "cors",
                "Sec-Fetch-Site":
                "same-origin",
                "User-Agent":
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0",
            },
                data={"data": encrypted_data_base64},
        ) as resp:
            if "access_token" in (data_json := resp.json()):
                self.RJ_headers[
                    "Authorization"] = "Bearer " + data_json["access_token"]
                print("登录成功")
            else:
                print("登录失败，请自行前往平台获取access_token")

    # ^获取平台数据请求参数
    def __Reptiles_rjReptilesParams(
        self,
        pagenum: int = 1,
        key: str = "",
        brandIds: List[int] = [],
        supplierIds: List[int] = [],
        sort: int = 3,
        pageSize: int = 20,
        relationCategoryOn: bool = True,
    ) -> ReptilesParamsModel:
        """
        获取平台数据请求参数

        参数：
         :param key: 搜索关键字
         :param brandIds: 品牌id
         :param pagenum: 页码
         :param supplierIds: 公司id
         :param sort: 排序方式
         :param pageSize: 一页展示的数量
         :param relationCategoryOn: 关系类型
         :return: 返回平台网站接口标准参数
        """
        return SearchParametersModel({
            "brandIds":
            brandIds if brandIds else [],
            "brandName":
            "",
            "casNo":
            "",
            "city":
            "",
            "orgId":
            "999",
            "pageSize":
            pageSize,
            "pageNo":
            pagenum,
            "sort":
            sort,
            "key":
            key,
            "maxPrice":
            "",
            "minPrice":
            "",
            "categoryIds": [],
            "searchFlag":
            11,
            "supplierIds":
            supplierIds if supplierIds else [],
            "supplierName":
            "",
            "flag":
            11,
            "relationCategoryOn":
            relationCategoryOn,
            "useDefaultSearchMode":
            False,
        })

    # ^获取其他商品列表中的商品详情
    def Reptiles_rjGetDetail(self, productId: int,
                             supplierId: int) -> rjReptilesModel:
        """
        获取其他商品列表中的商品详情
        :param productId: 需要查询详情的商品id
        :param supplierId: 该商品所在的公司id
        :return: 商品详情


        """
        print("获取锐竞商品详情：【商品id：{}】【公司id：{}】".format(productId, supplierId))
        while True:
            try:
                resp = self.session.post(
                    url=self.rj_urls["goodsDetail"],
                    headers=self.RJ_headers,
                    json={
                        "productId": productId,
                        "supplierId": supplierId
                    },
                    timeout=60,
                )
                data = resp.json()["data"]
                return data
            except:
                print("获取锐竞商品详情->重试")
                pass

    # ^获取平台其他店铺的商品目录页列表
    def Reptiles_rjGetGoodslist(
            self, pagenum: int,
            param: ReptilesParamsModel) -> List[rjReptilesCatalogueModel]:
        """
        获取平台其他店铺的商品目录页列表

        >>> from rjmart.parameModel import *
        >>> param:self.rjReptilesParams() | `ReptilesParamsModel`

        :param pagenum: 列表页码
        :param param: `ReptilesParamsModel`。
        :return: 商品列表`list`
        """
        print("获取平台其他店铺的商品目录：【页码：{}】".format(pagenum) + "".join([
            "【{key}:{value}】".format(key=key, value=param[key])
            for key in param
        ]))

        while True:
            try:
                with self.session.post(
                        url=self.rj_urls["goodsList"],
                        headers=self.RJ_headers,
                        json=(self.__Reptiles_rjReptilesParams(
                            pagenum, **param) if len(param) <= 7 else param),
                        timeout=30,
                ) as resp:
                    print(resp.json())
                    a = resp.json()["data"]
                    return a["products"] if a else []
            except Exception as e:
                print("获取平台其他店铺的商品目录页列表->重试", e)
                pass

    # ^搜索商品
    def Reptiles_rjGetSearch(
            self, productNum: str, supplierIds: List[int],
            brandIds: List[int]) -> Generator[rjReptilesModel, None, None]:
        """
        搜索商品

        :param productNum: 商品货号
        :param supplierId: 商品公司id
        :param brandIds: 品牌id
        :return: 搜索到的商品详情信息
        """
        with requests.post(
                url=self.rj_urls["goodsSearch"],
                headers=self.RJ_headers,
                json=(self.__Reptiles_rjReptilesParams(key=productNum,
                                                       supplierIds=supplierIds,
                                                       brandIds=brandIds,
                                                       sort=3)),
        ) as resp:
            products = resp.json()["data"]["products"]
            if products:
                for product in products:
                    yield self.Reptiles_rjGetDetail(
                        productId=product["id"],
                        supplierId=product["supplierId"])
            else:
                yield None
